home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir41 / tsrsrc35.zip / DISABLE.PAS < prev    next >
Pascal/Delphi Source File  |  1993-10-21  |  19KB  |  585 lines

  1. {**************************************************************************
  2. *   DISABLE - Activates or deactivates TSRs.                              *
  3. *   Copyright (c) 1987,1993 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   version 2.3 5/4/87                                                    *
  7. *     first release. version number matches other TSR Utilities           *
  8. *   :                                                                     *
  9. *   long intervening history                                              *
  10. *   :                                                                     *
  11. *   version 3.0 9/24/91                                                   *
  12. *     update for DOS 5                                                    *
  13. *     add Quiet option                                                    *
  14. *     add support for high memory                                         *
  15. *   version 3.1 11/4/91                                                   *
  16. *     update for new WATCH detection method                               *
  17. *   version 3.2 11/22/91                                                  *
  18. *     change method of accessing high memory                              *
  19. *   version 3.3 1/8/92                                                    *
  20. *     find TSRs by name just like MAPMEM does                             *
  21. *     increase stack space                                                *
  22. *     add /H to use high memory optionally                                *
  23. *     new features for parsing and getting command line options           *
  24. *   version 3.4 2/14/92                                                   *
  25. *     add /L option to turn off low memory checking                       *
  26. *   version 3.5 10/18/93                                                  *
  27. *     no change                                                           *
  28. ***************************************************************************
  29. *   telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  30. *   requires Turbo Pascal version 6 or 7 to compile.                      *
  31. ***************************************************************************}
  32.  
  33. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  34. {$M 4096,0,655360}
  35. {.$DEFINE MeasureStack}  {Activate to measure stack usage}
  36.  
  37. program DisableTSR;
  38.   {-Deactivate and reactivate memory resident programs}
  39.   {-Leaving them in memory all the while}
  40.  
  41. uses
  42.   Dos,
  43.   MemU;
  44.  
  45. var
  46.   Blocks : BlockArray;
  47.   BlockMax : BlockType;
  48.   WatchPsp : Word;
  49.   HiMemSeg : Word;
  50.   Changes : ChangeArray;
  51.   ChangeMax, ActualMax, PspHex, StartMCB : Word;
  52.   Action : (aDeactivate, aActivate, aCheckFor);
  53.   Override : Boolean;
  54.   Quiet : Boolean;
  55.   UseLoMem, OptUseHiMem, UseHiMem : Boolean;
  56.   TsrName : PathStr;
  57.   {$IFDEF MeasureStack}
  58.   I : Word;
  59.   {$ENDIF}
  60.  
  61.   procedure Abort(msg : String; ErrorLevel : Byte);
  62.     {-Halt in case of error}
  63.   begin
  64.     WriteLn(msg);
  65.     Halt(ErrorLevel);
  66.   end;
  67.  
  68.   function ExecutableBlock(PspHex : Word) : Boolean;
  69.     {-Return true if psphex corresponds to an executable code block}
  70.   var
  71.     b : BlockType;
  72.   begin
  73.     for b := BlockMax downto 1 do
  74.       {Search back to find executable rather than environment block}
  75.       if Blocks[b].psp = PspHex then begin
  76.         ExecutableBlock := True;
  77.         Exit;
  78.       end;
  79.     ExecutableBlock := False;
  80.   end;
  81.  
  82.   procedure InitChangeArray(WatchPsp : Word);
  83.     {-Initialize information regarding the WATCH data block}
  84.   var
  85.     watchindex : Word;
  86.     p : ^ChangeBlock;
  87.   begin
  88.     {Maximum offset in WATCH data area}
  89.     ActualMax := MemW[WatchPsp:NextChange];
  90.  
  91.     {Transfer changes from WATCH into a buffer array}
  92.     watchindex := 0;
  93.     ChangeMax := 0;
  94.     while watchindex < ActualMax do begin
  95.       p := Ptr(WatchPsp, ChangeVectors+watchindex);
  96.       Move(p^, Changes[ChangeMax], SizeOf(ChangeBlock));
  97.       Inc(watchindex, SizeOf(ChangeBlock));
  98.       if watchindex < ActualMax then
  99.         inc(ChangeMax);
  100.     end;
  101.   end;
  102.  
  103.   procedure PutWatch(chg : ChangeBlock; var watchindex : Word);
  104.     {-Put a change block back into WATCH}
  105.   var
  106.     p : ^ChangeBlock;
  107.   begin
  108.     p := Ptr(WatchPsp, ChangeVectors+watchindex);
  109.     Move(chg, p^, SizeOf(ChangeBlock));
  110.     Inc(watchindex, SizeOf(ChangeBlock));
  111.   end;
  112.  
  113.   procedure ActivateTSR(PspHex : Word);
  114.     {-Patch out the active interrupt vectors of a specified TSR}
  115.   var
  116.     nextchg, chg, watchindex : Word;
  117.     checking, didsomething : Boolean;
  118.   begin
  119.     didsomething := False;
  120.     watchindex := 0;
  121.     chg := 0;
  122.  
  123.     {Scan looking for the specified PSP}
  124.     while chg <= ChangeMax do begin
  125.       with Changes[chg] do
  126.         case ID of
  127.  
  128.           $FF :               {This record starts a new PSP}
  129.             begin
  130.               checking := (PspAdd = PspHex);
  131.               nextchg := Succ(chg);
  132.               if checking then
  133.                 {Turn off interrupts}
  134.                 inline($FA)
  135.               else
  136.                 {Turn on interrupts}
  137.                 inline($FB);
  138.             end;
  139.  
  140.           $01 :               {This record has an inactive vector redefinition}
  141.             if checking then begin
  142.               {We're in the proper PSP}
  143.               didsomething := True;
  144.               {Change the ID to indicate that vector is active}
  145.               ID := 0;
  146.               {Put the original vector code back in place}
  147.               nextchg := Succ(chg);
  148.               if (Changes[nextchg].ID <> 2) or (Changes[nextchg].VecNum <> VecNum) then
  149.                 Abort('Program error in Activate, patch record not found', 255);
  150.               {Restore the patched over code}
  151.               Move(Changes[nextchg].SaveCode, Mem[VecSeg:VecOfs], 6);
  152.               {Don't output the following patch record}
  153.               inc(nextchg);
  154.             end else
  155.               nextchg := Succ(chg);
  156.  
  157.         else
  158.           nextchg := Succ(chg);
  159.         end;
  160.  
  161.       {Put the change block back into WATCH}
  162.       PutWatch(Changes[chg], watchindex);
  163.       {Advance to the next change record}
  164.       chg := nextchg;
  165.     end;
  166.  
  167.     {Store the count back into WATCH}
  168.     MemW[WatchPsp:NextChange] := watchindex;
  169.  
  170.     if not(didsomething) then
  171.       Abort('No changes were needed to activate '+HexW(PspHex), 1);
  172.  
  173.   end;
  174.  
  175.   procedure DeactivateTSR(PspHex : Word);
  176.     {-Patch out the active interrupt vectors of a specified TSR}
  177.   var
  178.     newchange : ChangeBlock;
  179.     chg, watchindex, curpsp : Word;
  180.     putrec, checking, didsomething : Boolean;
  181.  
  182.     procedure PutPatch(vecn : Byte; vecs, veco, curpsp : Word);
  183.       {-Patch vector entry point with JMP to previous controlling vector}
  184.     label
  185.       ExitPoint;
  186.     var
  187.       vec : ^Word;
  188.       chg : Word;
  189.     begin
  190.       {Get the original vector from WATCH}
  191.       Move(Mem[WatchPsp:(OrigVectors+(vecn shl 2))], vec, 4);
  192.  
  193.       {Scan the Changes array to look for redefinition of this vector}
  194.       for chg := 0 to ChangeMax do begin
  195.         with Changes[chg] do
  196.           case ID of
  197.             0, 1 :            {This is or was a redefined vector}
  198.               if vecn = VecNum then
  199.                 {It's the vector we're interested in}
  200.                 {Store the latest value of the vector}
  201.                 Move(VecOfs, vec, 4);
  202.             $FF :             {This record starts a new PSP}
  203.               if PspAdd = curpsp then
  204.                 {Stop when we get to the PSP that is being disabled}
  205.                 goto ExitPoint;
  206.           end;
  207.       end;
  208. ExitPoint:
  209.       {Patch the vector entry point into a JMP FAR vec}
  210.       Mem[vecs:veco] := $EA;
  211.       Move(vec, Mem[vecs:Succ(veco)], 4);
  212.     end;
  213.  
  214.     function CountVecs(chg : Word) : Word;
  215.       {-Return count of vectors taken over by the PSP starting at changeblock chg}
  216.     var
  217.       count : Word;
  218.       ID : Byte;
  219.     begin
  220.       count := 0;
  221.       repeat
  222.         {Skip over the first one, which defines the current PSP}
  223.         inc(chg);
  224.         ID := Changes[chg].ID;
  225.         if (ID = 0) and (chg <= ChangeMax) then
  226.           inc(count);
  227.       until (ID = $FF) or (chg >= ChangeMax);
  228.       CountVecs := count;
  229.     end;
  230.  
  231.     function ValidToPatch(chg : Word) : Boolean;
  232.       {-Assure that there is space to place 6-byte patches}
  233.     var
  234.       First : Word;
  235.       Next : Word;
  236.       I : Word;
  237.       J : Word;
  238.       IAddr : LongInt;
  239.       JAddr : LongInt;
  240.     begin
  241.       ValidToPatch := True;
  242.       if Override then
  243.         Exit;
  244.  
  245.       {First vector to patch}
  246.       First := chg+1;
  247.  
  248.       {Last vector to patch}
  249.       Next := First;
  250.       while (Next <= ChangeMax) and (Changes[Next].ID <> $FF) do
  251.         inc(Next);
  252.  
  253.       {Any to patch?}
  254.       if Next = First then
  255.         Exit;
  256.  
  257.       {Compare each pair to assure enough space for patch}
  258.       for I := First to Next-1 do begin
  259.         with Changes[I] do
  260.           IAddr := (LongInt(VecSeg) shl 4)+VecOfs;
  261.         for J := First to Next-1 do
  262.           if I <> J then begin
  263.             with Changes[J] do
  264.               JAddr := (LongInt(VecSeg) shl 4)+VecOfs;
  265.             if Abs(IAddr-JAddr) < 6 then begin
  266.               ValidToPatch := False;
  267.               Exit;
  268.             end;
  269.           end;
  270.       end;
  271.     end;
  272.  
  273.   begin
  274.  
  275.     {Scan looking for the specified PSP}
  276.     didsomething := False;
  277.     watchindex := 0;
  278.  
  279.     for chg := 0 to ChangeMax do begin
  280.       putrec := True;
  281.       with Changes[chg] do
  282.         case ID of
  283.  
  284.           $FF :               {This record starts a new PSP}
  285.             begin
  286.               checking := (PspAdd = PspHex);
  287.               if checking then begin
  288.                 {Store the current PSP}
  289.                 curpsp := PspAdd;
  290.                 {Make sure WATCH has room for the extra changes}
  291.                 if watchindex+(CountVecs(chg)*SizeOf(ChangeBlock)) >
  292.                 MaxChanges*SizeOf(ChangeBlock) then
  293.                   Abort('Insufficient space in WATCH data area', 255);
  294.                 {Make sure the patches will be valid}
  295.                 if not ValidToPatch(chg) then
  296.                   Abort('Insufficient space between vectors to patch TSR', 255);
  297.                 {Turn off interrupts}
  298.                 inline($FA);
  299.               end else
  300.                 {Turn on interrupts}
  301.                 inline($FB);
  302.             end;
  303.  
  304.           $00 :               {This record has an active vector redefinition}
  305.             if checking then begin
  306.               {We're in the proper PSP}
  307.               didsomething := True;
  308.  
  309.               {Change the ID to indicate that vector is inactive}
  310.               ID := 1;
  311.               {Output the record now so that the new record can immediately follow}
  312.               PutWatch(Changes[chg], watchindex);
  313.               putrec := False;
  314.  
  315.               {Output a new change record so we can reactivate later}
  316.               {Indicate this is a patch record}
  317.               newchange.ID := 2;
  318.               {Save which vector it goes with}
  319.               newchange.VecNum := VecNum;
  320.               {Save the code we'll patch over}
  321.               Move(Mem[VecSeg:VecOfs], newchange.SaveCode, 6);
  322.               {Output the record to the WATCH area}
  323.               PutWatch(newchange, watchindex);
  324.               {Patch in a JMP to the previous vector}
  325.               PutPatch(VecNum, VecSeg, VecOfs, curpsp);
  326.             end;
  327.  
  328.         end;
  329.       if putrec then
  330.         {Put the change block back into WATCH}
  331.         PutWatch(Changes[chg], watchindex);
  332.     end;
  333.  
  334.     {Store the count back into WATCH}
  335.     MemW[WatchPsp:NextChange] := watchindex;
  336.  
  337.     if not(didsomething) then
  338.       Abort('No changes were needed to deactivate '+tsrname, 1);
  339.  
  340.   end;
  341.  
  342.   procedure CheckUpperLowerOptions;
  343.     {-Set low and high memory options}
  344.   var
  345.     Arg : String[127];
  346.  
  347.     procedure GetArgs(S : String);
  348.     var
  349.       SPos : Word;
  350.     begin
  351.       SPos := 1;
  352.       repeat
  353.         Arg := StUpcase(NextArg(S, SPos));
  354.         if Arg = '' then
  355.           Exit;
  356.         if (Arg = '-U') or (Arg = '/U') then
  357.           UseHiMem := True
  358.         else if (Arg = '-H') or (Arg = '/H') then
  359.           OptUseHiMem := True
  360.         else if (Arg = '-L') or (Arg = '/L') then
  361.           UseLoMem := False;
  362.       until False;
  363.     end;
  364.  
  365.   begin
  366.     UseHiMem := False;
  367.     OptUseHiMem := False;
  368.     UseLoMem := True;
  369.  
  370.     {Get arguments from the command line and the environment}
  371.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  372.     GetArgs(GetEnv('DISABLE'));
  373.   end;
  374.  
  375.   procedure GetOptions;
  376.     {-Analyze command line for options}
  377.  
  378.     procedure WriteCopyright;
  379.     begin
  380.       WriteLn('DISABLE ', Version, ', Copyright 1993 TurboPower Software');
  381.     end;
  382.  
  383.     procedure WriteHelp;
  384.       {-Show the options}
  385.     begin
  386.       WriteCopyright;
  387.       WriteLn;
  388.       WriteLn('DISABLE allows you to selectively disable and reenable a TSR while leaving it');
  389.       WriteLn('in memory. To run DISABLE, you must have previously installed the TSR utility');
  390.       WriteLn('WATCH.');
  391.       WriteLn;
  392.       WriteLn('DISABLE is command-line driven. You specify a single TSR by its name (if you');
  393.       WriteLn('are running DOS 3.0 or later) or by its address as determined from a MAPMEM');
  394.       WriteLn('report. Addresses must be preceded by a dollar sign "$" and specified in hex.');
  395.       WriteLn;
  396.       WriteLn('DISABLE accepts the following command line syntax:');
  397.       WriteLn;
  398.       WriteLn('  DISABLE TSRname|$PSPaddress [Options]');
  399.       WriteLn;
  400.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  401.       WriteLn;
  402.       WriteLn('  /A         reactivate the specified TSR.');
  403.       WriteLn('  /C         check whether TSR is installed.');
  404.       WriteLn('  /H         work with upper memory if available.');
  405.       WriteLn('  /O         disable the TSR even if dangerous.');
  406.       WriteLn('  /Q         write no screen output.');
  407.       WriteLn('  /U         work with upper memory, but halt if none found.');
  408.       WriteLn('  /?         write this help screen.');
  409.       Halt(1);
  410.     end;
  411.  
  412.     function FindOwner(tname : String) : Word;
  413.       {-Return segment of executable block with specified name}
  414.     var
  415.       b : BlockType;
  416.       IsCmd : Boolean;
  417.       M : McbPtr;
  418.       Name : String[79];
  419.     begin
  420.       tname := StUpcase(tname);
  421.  
  422.       {Scan the blocks in reverse order}
  423.       for b := BlockMax downto 1 do
  424.         with Blocks[b] do
  425.           if Succ(mcb) = psp then begin
  426.             {This block is an executable block}
  427.             IsCmd := (Psp = MemW[Psp:$16]);
  428.             M := Ptr(Mcb, 0);
  429.             if (not IsCmd) and (DosV > 2) and HasEnvironment(HiMemSeg, M) then
  430.               Name := NameFromEnv(M)
  431.             else if DosV >= 4 then
  432.               Name := NameFromMcb(M)
  433.             else if (not IsCmd) and (DosVT >= $031E) then
  434.               Name := NameFromMcb(M)
  435.             else
  436.               Name := '';
  437.             if StUpcase(Name) = tname then begin
  438.               FindOwner := Psp;
  439.               Exit;
  440.             end;
  441.           end;
  442.       FindOwner := $FFFF;
  443.     end;
  444.  
  445.     procedure GetArgs(S : String);
  446.     var
  447.       SPos : Word;
  448.       Code : Word;
  449.       Arg : String[127];
  450.     begin
  451.       SPos := 1;
  452.       repeat
  453.         Arg := NextArg(S, SPos);
  454.         if Arg = '' then
  455.           Exit;
  456.         if (Arg[1] = '?') then
  457.           WriteHelp
  458.         else if (Arg[1] = '-') or (Arg[1] = '/') then
  459.           case Length(Arg) of
  460.             1 : Abort('Missing command option following '+Arg, 254);
  461.             2 : case UpCase(Arg[2]) of
  462.                   '?' : WriteHelp;
  463.                   'A' : Action := aActivate;
  464.                   'C' : Action := aCheckFor;
  465.                   'E' : Action := aActivate;
  466.                   'H' : ; {ignore, but allow, here}
  467.                   'L' : ; {ignore, but allow, here}
  468.                   'O' : Override := True;
  469.                   'Q' : Quiet := True;
  470.                   'U' : ; {ignore, but allow, here}
  471.                 else
  472.                   Abort('Unknown command option: '+Arg, 254);
  473.                 end;
  474.           else
  475.             Abort('Unknown command option: '+Arg, 254);
  476.           end
  477.         else begin
  478.           {TSR to change}
  479.           if Arg[1] = '$' then begin
  480.             {Treat as hex address}
  481.             Val(Arg, PspHex, Code);
  482.             if Code <> 0 then
  483.               Abort('Invalid hex address specification: '+Arg, 254);
  484.           end else if DosV >= 3 then
  485.             {Treat as PSP owner name - scan to find proper PSP}
  486.             PspHex := FindOwner(Arg)
  487.           else
  488.             Abort('Must have DOS 3.0+ to find TSRs by name', 254);
  489.           TsrName := StUpcase(Arg);
  490.         end;
  491.       until False;
  492.     end;
  493.  
  494.   begin
  495.     {Initialize defaults}
  496.     PspHex := 0;
  497.     Action := aDeactivate;
  498.     Override := False;
  499.     Quiet := False;
  500.  
  501.     {Get arguments from the command line and the environment}
  502.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  503.     GetArgs(GetEnv('DISABLE'));
  504.  
  505.     if not Quiet then
  506.       WriteCopyright;
  507.     if PspHex = 0 then
  508.       Abort('No TSR name or address specified', 254)
  509.     else if PspHex = $FFFF then
  510.       if (Action = aCheckFor) and Quiet then   {!!}
  511.         Halt(2)                                {!!}
  512.       else                                     {!!}
  513.         Abort('Did not find '+TsrName, 2);
  514.   end;
  515.  
  516. begin
  517.   {$IFDEF MeasureStack}
  518.   FillChar(Mem[SSeg:0], SPtr-16, $AA);
  519.   {$ENDIF}
  520.  
  521.   {Determine whether upper memory control is desired}
  522.   CheckUpperLowerOptions;
  523.  
  524.   {Initialize for high memory access}
  525.   if not UseLoMem then
  526.     OptUseHiMem := True;
  527.   if OptUseHiMem or UseHiMem then begin
  528.     HiMemSeg := FindHiMemStart;
  529.     if HiMemSeg = 0 then begin
  530.       if UseHiMem then
  531.         Abort('No upper memory blocks found', 255);
  532.     end else
  533.       UseHiMem := True;
  534.   end else
  535.     HiMemSeg := 0;
  536.  
  537.   {Get all allocated memory blocks in normal memory}
  538.   {Must do first to support TSRs by name in GetOptions}
  539.   FindTheBlocks(UseLoMem, HiMemSeg, Blocks, BlockMax, StartMcb);
  540.  
  541.   {Analyze command line for options}
  542.   GetOptions;
  543.  
  544.   {Find the watch block}
  545.   WatchPsp := WatchPspSeg;
  546.   if WatchPsp = 0 then
  547.     Abort('WATCH must be installed in order to use DISABLE', 255);
  548.  
  549.   {Assure PspHex corresponds to an executable block}
  550.   if not ExecutableBlock(PspHex) then
  551.     Abort('No such TSR found', 2);
  552.  
  553.   {Initialize information regarding the WATCH data block}
  554.   InitChangeArray(WatchPsp);
  555.  
  556.   {Activate or deactivate the TSR}
  557.   case Action of
  558.     aDeactivate:DeactivateTSR(PspHex);
  559.     aActivate:ActivateTSR(PspHex);
  560.   end;
  561.  
  562.   {Write success message}
  563.   if not Quiet then begin
  564.     case Action of
  565.       aDeactivate:Write('Deactivated');
  566.       aActivate:Write('Activated');
  567.       aCheckFor:Write('Found');
  568.     end;
  569.     Write(' ');
  570.     if TsrName[1] = '$' then
  571.       Write('TSR at ');
  572.     WriteLn(TsrName);
  573.   end;
  574.  
  575.   {$IFDEF MeasureStack}
  576.   I := 0;
  577.   while I < SPtr-16 do
  578.     if Mem[SSeg:i] <> $AA then begin
  579.       writeln('Unused stack ', i, ' bytes');
  580.       I := SPtr;
  581.     end else
  582.       inc(I);
  583.   {$ENDIF}
  584. end.
  585.